package com.caissa.framework.starter.autoconfigure;

import com.caissa.framework.starter.autoconfigure.annotation.CatAopAdvise;
import com.caissa.framework.starter.autoconfigure.common.CatProperties;
import com.caissa.framework.starter.autoconfigure.container.CustomContainer;
import com.caissa.framework.starter.autoconfigure.db.DruidDataSourceMetrics;
import com.caissa.framework.starter.autoconfigure.instrument.aop.spring.CaissaFacadeAdvise;
import com.caissa.framework.starter.autoconfigure.instrument.aop.spring.CaissaServiceAdvise;
import com.caissa.framework.starter.autoconfigure.instrument.aop.spring.MyBatisPlusAdvise;
import com.caissa.framework.starter.autoconfigure.instrument.aop.spring.SpringWebHandlerAdvise;
import com.caissa.framework.starter.autoconfigure.jmx.KafkaProducerMetrics;
import com.caissa.framework.starter.autoconfigure.reference.CatFeignInterceptor;
import com.caissa.framework.starter.autoconfigure.reference.CatRestInterceptor;
import com.caissa.framework.starter.autoconfigure.reference.DruidCatFilter;
import com.caissa.framework.starter.autoconfigure.reference.KafkaConsumerCatAspect;
import com.dianping.cat.CatConstants;
import io.github.mweirauch.micrometer.jvm.extras.ProcessMemoryMetrics;
import io.github.mweirauch.micrometer.jvm.extras.ProcessThreadMetrics;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.binder.MeterBinder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.autoconfigure.metrics.MeterRegistryCustomizer;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;

import javax.annotation.Resource;

import static com.dianping.cat.CatConstants.CAISSA_CAT_DOMAIN;

/**
 * apm 启动器自动装配
 *
 * @author wangwei
 * @date 2020/8/18
 */
@Configuration
@ConditionalOnClass(name = "com.dianping.cat.Cat")
@EnableConfigurationProperties(CatProperties.class)
@Slf4j
public class APMAutoConfiguration {

    @Autowired
    private CatProperties catProperties;

    @Resource
    private Environment environment;

    @Bean
    public CatFeignInterceptor catFeignInterceptor(){
        return new CatFeignInterceptor();
    }

    @Bean
    public CatRestInterceptor catRestInterceptor(){
        return new CatRestInterceptor();
    }

    private void initCatParam() {
        //容器先于CAT启动,在此将配置传给CAT
        String serverAddrsSysPro = System.getProperty(CatConstants.CAISSA_CAT_SERVER_LIST);
        String serverAddrs = catProperties.getServerAddrs();
        if (serverAddrsSysPro == null && serverAddrs != null) {
            log.info("cat.config.serverAddrs: " + serverAddrs);
            System.setProperty(CatConstants.CAISSA_CAT_SERVER_LIST, serverAddrs);
        }

        String catDomainSysPro = System.getProperty(CAISSA_CAT_DOMAIN);
        String catDomain = environment.getProperty(CAISSA_CAT_DOMAIN);
        if (catDomainSysPro == null && catDomain != null) {
            log.info("cat.domain: " + catDomain);
            System.setProperty(CAISSA_CAT_DOMAIN, catDomain);
        }
    }

    @Bean
    @ConditionalOnClass(name="org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter")
    public SpringWebHandlerAdvise springWebHandlerAdvise(){
        initCatParam();
        return new SpringWebHandlerAdvise();
    }

    @Bean
    public CatAopAdvise catAopAdvise(){
        initCatParam();
        return new CatAopAdvise();
    }

    @Bean
    @ConditionalOnClass(name="com.baomidou.mybatisplus.core.mapper.BaseMapper")
    public MyBatisPlusAdvise myBatisPlusAdvise(){
        initCatParam();
        return new MyBatisPlusAdvise();
    }

    @Bean
    public CaissaServiceAdvise caissaServiceAdvise(){
        initCatParam();
        return new CaissaServiceAdvise();
    }

    @Bean
    public CaissaFacadeAdvise caissaFacadeAdvise(){
        initCatParam();
        return new CaissaFacadeAdvise();
    }

    @Bean
    @ConditionalOnClass(name = "com.base.framework.starter.autoconfigure.jmx.KafkaProducerMetrics")
    public KafkaProducerMetrics kafkaProducerMetrics() {
        return new KafkaProducerMetrics();
    }

    @Bean
    @ConditionalOnClass(name = "org.springframework.kafka.annotation.KafkaListener")
    public KafkaConsumerCatAspect kafkaConsumerCatAspect(){
        return new KafkaConsumerCatAspect();
    }

    @Bean
    @ConditionalOnClass(name = "com.alibaba.druid.filter.FilterChain")
    public DruidCatFilter druidCatFilter(){
        return new DruidCatFilter();
    }

    @Bean
    @ConditionalOnClass(name = "com.alibaba.druid.stat.DruidStatManagerFacade")
    public DruidDataSourceMetrics druidDataSourceMetrics(){
        return new DruidDataSourceMetrics();
    }

    @Bean
    @ConditionalOnClass(name = "io.micrometer.core.instrument.MeterRegistry")
    public MeterRegistryCustomizer<MeterRegistry> metricsCommonTags(){
        String domain = environment.getProperty(CAISSA_CAT_DOMAIN);
        return registry -> registry.config().commonTags("application", domain != null ? domain : "UNKNOWN");
    }

    @Bean
    @ConditionalOnClass(name = "io.github.mweirauch.micrometer.jvm.extras.ProcessThreadMetrics")
    public MeterBinder processMemoryMetrics() {
        return new ProcessMemoryMetrics();
    }

    @Bean
    @ConditionalOnClass(name = "io.github.mweirauch.micrometer.jvm.extras.ProcessThreadMetrics")
    public MeterBinder processThreadMetrics() {
        return new ProcessThreadMetrics();
    }

    @Bean
    @ConditionalOnClass(name = {
            "org.springframework.boot.web.server.WebServerFactoryCustomizer",
            "org.apache.coyote.http11.Http11NioProtocol"
    })
    public CustomContainer customContainer() {
        return new CustomContainer();
    }

    @Bean
    public void checkAspectJWeaver() {
        try {
            Class.forName("org.aspectj.weaver.loadtime.Agent");
            log.info("AspectJ weaving agent loaded from -javaagent:/path/to/aspectjweaver-{version}.jar");
        } catch (ClassNotFoundException e) {
            log.warn("AspectJ weaving agent not loaded,check -javaagent:/path/to/aspectjweaver-{version}.jar");
        }
    }
}